import time
import random

import requests
from django.conf import settings
from rest_framework import status
from django.db import transaction
from django.db.models import F, Max
from rest_framework import exceptions
from xml.etree import ElementTree as ET
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.generics import ListAPIView, CreateAPIView, RetrieveAPIView, UpdateAPIView

from api import models
from utils.common.auth import JWTAuthentication
from api.serializer.pay import MoneyTypeModelSerializer, AuctionItemPriceModelSerializer, OrderModelSerializer


class MoneyTypeAPIView(ListAPIView):
    """支持类型渲染视图"""
    authentication_classes = [JWTAuthentication]
    queryset = models.DepositRecord.objects.filter(is_delete=False, is_show=True)
    serializer_class = MoneyTypeModelSerializer


# 进行加密(不能加盐)，否则会出现签名问题
def md5(string):
    """md5加密"""
    import hashlib
    m = hashlib.md5()
    m.update(string.encode('utf-8'))
    return m.hexdigest()


# 充值
class PaymentView(APIView):
    """支付视图及操作"""
    authentication_classes = [JWTAuthentication]

    def post(self, request, *args, **kwargs):
        # 前端传送过来的商品id
        m_id = request.data.get('moneyTypeId')
        if not m_id:
            raise exceptions.ValidationError('数据不合法')
        money_type_id = int(m_id)
        # print(money_type_id)
        if money_type_id not in [1, 2, 3, 4, 5, 6, 7]:
            raise exceptions.ValidationError('数据不合法')
        # 商户订单号
        order_random_string = str(int(time.time()))
        # 商品的对象
        money_type_object = models.DepositRecord.objects.filter(id=money_type_id).first()  # goods_object.price
        # 创建一个订单
        order_object = models.RechargeRecord.objects.create(uid=order_random_string, amount=money_type_object.amount,
                                                            deposit_record=money_type_object, user=request.user)

        # 按照微信的规则，去生成支付需要的一大堆的数据
        # https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1
        # print(order_random_string)
        # ###################### 1.调用支付统一下单 ######################
        info = {
            # 小程序id T
            'appid': settings.WX_APPID,
            # 商户关联id T
            'mch_id': settings.WX_MCH_ID,
            # 设备号 F
            'device_info': 'ymq-text',
            # 随机字符串 T
            'nonce_str': "".join([chr(random.randint(65, 90)) for _ in range(12)]),
            # 签名类型 F
            'sign_type': "MD5",
            # 商品描述 F
            'body': "充值",
            # 商品详情 F
            'detail': '充值操作',
            # 附加数据 F
            'attach': '充值上线',
            # 商户订单号 T
            'out_trade_no': order_object.uid,
            # 标价金额 T
            'total_fee': money_type_object.amount,  # 总金额
            # 终端IP(用户ip) F
            'spbill_create_ip': request.META.get('REMOTE_ADDR'),
            # # 支付成功之后，微信异步通知(post) T
            'notify_url': "https://api.0528.ltd/api/pay/notify/",
            # 交易类型 T
            'trade_type': 'JSAPI',
            # 用户标识(trade_type=JSAPI，此参数必传)
            'openid': request.user.openid
        }
        # 1.1 签名
        #       对字典中的key按照ASCII码从小到大排序
        #       将排完序的值拼接 stringA = appid=wx55cca0b94f723dc7&mch_id=1526049051
        #       让stringA和key拼接：stringSignTemp = stringA+"&key=192006250b4c09247ec02edce69f6a2d" key为商户平台设置的密钥key
        #       MD5(stringSignTemp)
        #       将密文转换为大写
        #       得到签名 sign
        #       把签名再添加到info中    info['sign'] = sign值

        # 商户密钥key
        pay_key = settings.WX_PAY_KEY
        #
        temp = "&".join(["{0}={1}".format(k, info[k]) for k in sorted(info)] + ["{0}={1}".format("key", pay_key, ), ])
        sign = md5(temp).upper()
        info['sign'] = sign
        # 1.2 向 https://api.mch.weixin.qq.com/pay/unifiedorder 发请求 (json转换为xml)

        xml_string = "<xml>{0}</xml>".format("".join(["<{0}>{1}</{0}>".format(k, v) for k, v in info.items()]))
        prepay = requests.post('https://api.mch.weixin.qq.com/pay/unifiedorder', data=xml_string.encode('utf-8'))
        # 1.3 从结果xml中提取 prepay_id

        root = ET.XML(prepay.content.decode('utf-8'))
        prepay_dict = {child.tag: child.text for child in root}
        # print("prepay_dict", prepay_dict)
        prepay_id = prepay_dict['prepay_id']

        # ####################### 2.再次签名 #######################
        info_dict = {
            # 小程序的appid
            'appId': settings.WX_APPID,
            'timeStamp': str(int(time.time())),  # 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
            'nonceStr': "".join([chr(random.randint(65, 90)) for _ in range(12)]),  # 随机字符串，长度为32个字符以下。
            'package': 'prepay_id={0}'.format(prepay_id),  # 统一下单接口返回的 prepay_id 参数值，提交格式如：prepay_id=*
            'signType': 'MD5',  # 签名类型，默认为MD5，支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
        }
        temp = "&".join(
            ["{0}={1}".format(k, info_dict[k]) for k in sorted(info_dict)] + ["{0}={1}".format("key", pay_key, ), ])
        sign2 = md5(temp).upper()
        info_dict['paySign'] = sign2

        # print(info_dict)
        return Response(info_dict)


# 成功回调
class NotifyView(APIView):
    """
    支付完成之后的通知
    """

    def post(self, request, *args, **kwargs):
        # 1. 获取结果把结果XML转换为字典格式
        root = ET.XML(request.body.decode('utf-8'))
        result = {child.tag: child.text for child in root}

        # 2. 校验签名是否正确，防止恶意请求。
        sign = result.pop('sign')

        # key为商户平台设置的密钥key
        key = settings.WX_PAY_KEY
        temp = "&".join(
            ["{0}={1}".format(k, result[k]) for k in sorted(result)] + ["{0}={1}".format("key", key, ), ])
        local_sign = md5(temp).upper()

        # 签名一致
        if local_sign == sign:
            # 根据订单号，把数据库的订单状态修改为支付成功
            out_trade_no = result.get('out_trade_no')
            # 如果支付成功，修改订单状态
            recharge_query = models.RechargeRecord.objects.filter(uid=out_trade_no)
            order_query = models.Order.objects.filter(uid=out_trade_no)
            if recharge_query:
                # 更新
                recharge_query.update(status=2)
                # 获取当前订单的用户
                recharge_obj = recharge_query.first(uid=out_trade_no)
                # 当前用户的id
                user_id = recharge_obj.user.id
                # 获取购买的额度
                money = recharge_obj.amount
                # 给当前用户的余额进行增加
                models.UserInfo.objects.filter(id=user_id).update(balance=F('balance') + money)
            elif order_query:
                # 修改订单状态
                order_query.update(status=2)
            response = """<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"""
            return Response(response)


# 获取最大出价&&竞价
class AuctionItemPriceAPIView(ListAPIView, CreateAPIView):
    """获取最大出价&&竞价视图
    GET:http://127.0.0.1:8090/api/get/price?item=2
    POST:http://127.0.0.1:8090/api/get/price?item=2
    {
    "price":260
    }
    """
    authentication_classes = [JWTAuthentication]
    queryset = models.BidRecord.objects.filter(is_delete=False, is_show=True, status=1)
    serializer_class = AuctionItemPriceModelSerializer

    def get_queryset(self):
        """添加过滤条件"""
        item_id = self.request.GET.get('item')
        # if not item_id:
        #     pass
        queryset = models.BidRecord.objects.filter(item_id=item_id)
        return queryset

    def list(self, request, *args, **kwargs):
        """重写list"""
        # 获取当前商品的最高价返回
        response = super(AuctionItemPriceAPIView, self).list(request, *args, **kwargs)
        item_id = self.request.GET.get('item')
        new_data = {
            'status': '',
            'max_price': '',
            'username': '',
            'msg': '成功'
        }
        if not item_id:
            response.data = new_data
            return response
        query_data = models.BidRecord.objects.filter(item_id=item_id, is_delete=False, is_show=True)
        # print(query_data)
        if not query_data:
            response.data = new_data
            return response
        max_price = query_data.aggregate(max_price=Max('price'))['max_price']
        # 当前出价人的姓名
        price_username = query_data.filter(price=max_price).first().user.username
        new_data['status'] = 1000
        new_data['max_price'] = max_price
        new_data['username'] = price_username
        response.data = new_data
        return response

    def perform_create(self, serializer):
        """保存之前做操作"""
        # 保存之前加锁
        # 在django中加锁需要现加事务
        with transaction.atomic():
            item_id = self.request.GET.get('item')
            price = int(self.request.data.get('price'))
            # 加悲观锁，同一时间，只能一个进来  select_for_update
            # 如果一开始没有价格会报错
            price_query = models.BidRecord.objects.filter(status=1, is_delete=False, is_show=True,
                                                          item_id=item_id).select_for_update()
            old_price = price_query.aggregate(max_price=Max('price')).get('max_price')
            if not old_price:
                serializer.save(user=self.request.user, item_id=item_id)
            if price < old_price:
                raise exceptions.ValidationError('当前价格低于最高价格')
            serializer.save(user=self.request.user, item_id=item_id)
            # 添加拍卖场的出价记录
            # 获取到拍卖场
            # 进行出价记录修改
            auction_query = models.AuctionItem.objects.filter(is_show=True, is_delete=False, status=3, id=item_id)
            auction_obj = auction_query.first().auction
            auction_obj.bid_count += 1
            # print('auction_obj.bid_count', auction_obj.bid_count)
            auction_obj.save()
            # 添加商品的出价记录
            # 获取到当前商品
            # 进行出价记录修改
            auction_query.update(
                bid_count=F('bid_count') + 1)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        new_data = {
            'status': '',
            'max_price': '',
            'username': '',
            'msg': ''
        }
        if serializer.is_valid():
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            # 修改返回的数据
            new_data['status'] = 1000
            new_data['max_price'] = request.data.get('price')
            new_data['username'] = request.user.username
            new_data['msg'] = '成功'
            return Response(new_data, status=status.HTTP_201_CREATED, headers=headers)
        item_id = request.GET.get('item')
        max_price = models.BidRecord.objects.filter(item_id=item_id, is_delete=False, is_show=True, status=1).order_by(
            '-id').first().price
        # print(max_price)
        new_data['status'] = 1007
        new_data['max_price'] = max_price
        new_data['username'] = request.user.username
        new_data['msg'] = serializer.errors
        return Response(new_data, status=status.HTTP_201_CREATED)


# 订单视图
class OrderAPIView(ListAPIView):
    """支付订单视图"""
    authentication_classes = [JWTAuthentication]
    queryset = models.Order.objects.filter(is_delete=False, is_show=True)
    serializer_class = OrderModelSerializer

    def get_queryset(self):
        select_id = self.request.query_params.get('select_id')
        order_status = 1
        if select_id == '1':
            order_status = 1
        elif select_id == '2':
            order_status = 2
        elif select_id == '3':
            order_status = 3
        else:
            raise exceptions.ValidationError('参数错误')
        # 返回自己的订单
        queryset = models.Order.objects.filter(status=order_status, userinfo=self.request.user).order_by('-id')
        return queryset


class PayOrderAPIView(ListAPIView):
    """订单支付"""
    authentication_classes = [JWTAuthentication]

    def post(self, request, *args, **kwargs):
        # 前端传送过来的订单id
        o_id = request.data.get('order_id')
        if not o_id:
            raise exceptions.ValidationError('数据不合法')
        order_id = int(o_id)
        if not order_id:
            raise exceptions.ValidationError('数据不合法')

        # 获取地址
        addr = request.data.get('addr')
        # print(order_id)
        # print(addr)
        consignee = addr.get('consignee')
        phone = addr.get('phone')
        position = addr.get('position')

        if not addr:
            raise exceptions.ValidationError('收货信息有误')
        if not consignee:
            raise exceptions.ValidationError('收货人有误')
        if not phone:
            raise exceptions.ValidationError('手机号有误')
        if not position:
            raise exceptions.ValidationError('收货信息有误')

        # 订单的对象
        order_query = models.Order.objects.filter(id=order_id, status=1,
                                                  userinfo=request.user)
        order_obj = order_query.first()
        if not order_obj:
            raise exceptions.ValidationError('数据不合法')

        # 给订单添加地址
        # order_query.update(**addr)
        models.Addr.objects.create(**addr, order=order_obj, userinfo=request.user)
        # 按照微信的规则，去生成支付需要的一大堆的数据
        # https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1
        # print(order_random_string)
        # ###################### 1.调用支付统一下单 ######################
        info = {
            # 小程序id T
            'appid': settings.WX_APPID,
            # 商户关联id T
            'mch_id': settings.WX_MCH_ID,
            # 设备号 F
            'device_info': 'x-lzz',
            # 随机字符串 T
            'nonce_str': "".join([chr(random.randint(65, 90)) for _ in range(12)]),
            # 签名类型 F
            'sign_type': "MD5",
            # 商品描述 F
            'body': "充值",
            # 商品详情 F
            'detail': '充值操作',
            # 附加数据 F
            'attach': '充值上线',
            # 商户订单号 T
            'out_trade_no': order_obj.uid,
            # 标价金额 T
            'total_fee': order_obj.price,  # 总金额
            # 终端IP(用户ip) F
            'spbill_create_ip': request.META.get('REMOTE_ADDR'),
            # # 支付成功之后，微信异步通知(post) T
            'notify_url': "http://106.14.42.256:8090/pay/notify/",
            # 交易类型 T
            'trade_type': 'JSAPI',
            # 用户标识(trade_type=JSAPI，此参数必传)
            'openid': request.user.openid
        }
        # 1.1 签名
        #       对字典中的key按照ASCII码从小到大排序
        #       将排完序的值拼接 stringA = appid=wx55cca0b94f723dc7&mch_id=1526049051
        #       让stringA和key拼接：stringSignTemp = stringA+"&key=192006250b4c09247ec02edce69f6a2d" key为商户平台设置的密钥key
        #       MD5(stringSignTemp)
        #       将密文转换为大写
        #       得到签名 sign
        #       把签名再添加到info中    info['sign'] = sign值

        # 商户密钥key
        pay_key = settings.WX_PAY_KEY
        #
        temp = "&".join(["{0}={1}".format(k, info[k]) for k in sorted(info)] + ["{0}={1}".format("key", pay_key, ), ])
        sign = md5(temp).upper()
        info['sign'] = sign
        # 1.2 向 https://api.mch.weixin.qq.com/pay/unifiedorder 发请求 (json转换为xml)

        xml_string = "<xml>{0}</xml>".format("".join(["<{0}>{1}</{0}>".format(k, v) for k, v in info.items()]))
        prepay = requests.post('https://api.mch.weixin.qq.com/pay/unifiedorder', data=xml_string.encode('utf-8'))
        # 1.3 从结果xml中提取 prepay_id

        root = ET.XML(prepay.content.decode('utf-8'))
        prepay_dict = {child.tag: child.text for child in root}
        # print("prepay_dict", prepay_dict)
        prepay_id = prepay_dict['prepay_id']

        # ####################### 2.再次签名 #######################
        info_dict = {
            # 小程序的appid
            'appId': settings.WX_APPID,
            'timeStamp': str(int(time.time())),  # 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
            'nonceStr': "".join([chr(random.randint(65, 90)) for _ in range(12)]),  # 随机字符串，长度为32个字符以下。
            'package': 'prepay_id={0}'.format(prepay_id),  # 统一下单接口返回的 prepay_id 参数值，提交格式如：prepay_id=*
            'signType': 'MD5',  # 签名类型，默认为MD5，支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
        }
        temp = "&".join(
            ["{0}={1}".format(k, info_dict[k]) for k in sorted(info_dict)] + ["{0}={1}".format("key", pay_key, ), ])
        sign2 = md5(temp).upper()
        info_dict['paySign'] = sign2

        # print(info_dict)
        return Response(info_dict)
